<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="cvYmW" id="cvYmW"><span data-lake-id="ub12d0cc6" id="ub12d0cc6">典型回答</span></h1>
  <p data-lake-id="u2fe3bafe" id="u2fe3bafe"><br></p>
  <p data-lake-id="u8813ffb8" id="u8813ffb8"><br></p>
  <p data-lake-id="u0009e6b8" id="u0009e6b8"><span data-lake-id="u2b6c9c24" id="u2b6c9c24">上面的题目中介绍过synchronized的实现机制，但是很多人还是不明白synchronized到底锁的是什么。因为synchronized在使用的时候，可以锁定对象，也可以锁定当前类，所以很多人会产生误会：</span></p>
  <p data-lake-id="ua97df37e" id="ua97df37e"><span data-lake-id="u1f9ed801" id="u1f9ed801">​</span><br></p>
  <pre lang="java"><code>
//同步方法，对象锁  
public synchronized void doSth(){
    System.out.println("Hello World");
}

//同步方法，类锁  
public synchronized static void doSth(){
    System.out.println("Hello World");
}


//同步代码块，类锁
public void doSth1(){
    synchronized (Demo.class){
        System.out.println("Hello World");
    }
}

//同步代码块，对象锁
public void doSth1(){
    synchronized (this){
        System.out.println("Hello World");
    }
}
</code></pre>
  <p data-lake-id="u1dbf3af8" id="u1dbf3af8"><br></p>
  <p data-lake-id="u9ad19a66" id="u9ad19a66"><span data-lake-id="u17b99915" id="u17b99915">但是，不管是上面哪种用法，</span><strong><span data-lake-id="u662f6304" id="u662f6304"> synchronized最终锁的都是对象！</span></strong><span data-lake-id="ued2f091c" id="ued2f091c">Java中一切皆对象，类最终也是通过对象的方式呈现的。所以，</span><strong><span data-lake-id="u30f55325" id="u30f55325">synchronized的不同用法，只不过锁的对象不同而已。</span></strong></p>
  <p data-lake-id="ubb19bed0" id="ubb19bed0"><span data-lake-id="ubf569feb" id="ubf569feb">​</span><br></p>
  <p data-lake-id="ubdb3fa0e" id="ubdb3fa0e"><span data-lake-id="ucf5d76f4" id="ucf5d76f4">并且，我们都知道synchronized加锁的过程需要再对象头上修改锁标记的，而这个对象头你就知道要依赖对象了。</span></p>
  <p data-lake-id="ue9a35131" id="ue9a35131"><span data-lake-id="u8dc220f2" id="u8dc220f2">​</span><br></p>
  <h3 data-lake-id="BIJM8" id="BIJM8"><span data-lake-id="u7d616927" id="u7d616927">同步方法</span></h3>
  <p data-lake-id="u4a2761d0" id="u4a2761d0"><span data-lake-id="u5d521dff" id="u5d521dff">synchronized修饰的同步方法有两种，一种是普通方法，一种是静态方法。</span></p>
  <p data-lake-id="u0e92adfd" id="u0e92adfd"><span data-lake-id="u703739a7" id="u703739a7">​</span><br></p>
  <p data-lake-id="u5bd9b9d9" id="u5bd9b9d9"><span data-lake-id="u4a64ab44" id="u4a64ab44">以下，是一个普通的同步方法：</span></p>
  <pre lang="java"><code>
public class ThreadTest {

    public static void main(String[] args) {

        for (int i = 0; i &lt; 10; i++) {
            MyThread myThread = new MyThread();
            Thread thread = new Thread(myThread);
            thread.start();

        }
    }
}

class MyThread implements Runnable {

    @Override
    public void run() {
        print();
    }

    public synchronized void print() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " : " + System.currentTimeMillis());
    }
}
</code></pre>
  <p data-lake-id="u04a11f60" id="u04a11f60"><br></p>
  <p data-lake-id="ub6753d04" id="ub6753d04"><span data-lake-id="u071f92d7" id="u071f92d7">这个代码在执行时，输出结果：</span></p>
  <p data-lake-id="u3537021b" id="u3537021b"><span data-lake-id="ucd01c288" id="ucd01c288">​</span><br></p>
  <pre lang="java"><code>
Thread-3 : 1692845687094
Thread-1 : 1692845687094
Thread-5 : 1692845687094
Thread-6 : 1692845687094
Thread-7 : 1692845687094
Thread-0 : 1692845687094
Thread-4 : 1692845687094
Thread-2 : 1692845687094
Thread-8 : 1692845687094
Thread-9 : 1692845687094
</code></pre>
  <p data-lake-id="uc3931755" id="uc3931755"><br></p>
  <p data-lake-id="u7360a249" id="u7360a249"><span data-lake-id="u5b592384" id="u5b592384">也就是说，多个线程之间是没有互相被锁阻塞而影响的，他们打印出来的时间戳都是一样的。</span></p>
  <p data-lake-id="u6b44f092" id="u6b44f092"><span data-lake-id="u5cf657e8" id="u5cf657e8">​</span><br></p>
  <p data-lake-id="ub79cba84" id="ub79cba84"><span data-lake-id="ua64a1379" id="ua64a1379">而，如果是同步静态方法就不一样了：</span></p>
  <pre lang="java"><code>
public class ThreadTest {

    public static void main(String[] args) {

        for (int i = 0; i &lt; 10; i++) {
            MyThread myThread = new MyThread();
            Thread thread = new Thread(myThread);
            thread.start();

        }
    }
}

class MyThread implements Runnable {

    @Override
    public void run() {
        print();
    }

    public static synchronized void print() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " : " + System.currentTimeMillis());
    }
}
</code></pre>
  <p data-lake-id="ubea07391" id="ubea07391"><br></p>
  <p data-lake-id="u31aaa7f5" id="u31aaa7f5"><span data-lake-id="u415fe86d" id="u415fe86d">以上代码在执行时，每一行内容是顺序并且按照一定的延迟打印出来的：</span></p>
  <p data-lake-id="ua2e2f2a4" id="ua2e2f2a4"><span data-lake-id="u14c0db03" id="u14c0db03">​</span><br></p>
  <pre lang="java"><code>
Thread-0 : 1692845747897
Thread-9 : 1692845748904
Thread-8 : 1692845749905
Thread-7 : 1692845750909
Thread-6 : 1692845751913
Thread-5 : 1692845752915
Thread-4 : 1692845753920
Thread-3 : 1692845754923
Thread-2 : 1692845755925
Thread-1 : 1692845756931
</code></pre>
  <p data-lake-id="u9fbc5637" id="u9fbc5637"><br></p>
  <p data-lake-id="uaf1cd803" id="uaf1cd803"><br></p>
  <p data-lake-id="u945c10a4" id="u945c10a4"><span data-lake-id="u262ea3a7" id="u262ea3a7">可以看到，每个线程打印出来的时间戳都不一样，是有时间间隔的，这是因为他们互相之间有锁阻塞的影响。</span></p>
  <p data-lake-id="u76a1277b" id="u76a1277b"><br></p>
  <p data-lake-id="u169df038" id="u169df038"><strong><span data-lake-id="ub779a66e" id="ub779a66e">总结一下，synchronized的普通方法，其实锁的是具体调用这个方法的实例对象，而synchronized的静态方法，其实锁的是这个方法锁属于的类对象。</span></strong></p>
  <p data-lake-id="ua20c6218" id="ua20c6218"><span data-lake-id="u0edf589c" id="u0edf589c">​</span><br></p>
  <blockquote data-lake-id="u1636a778" id="u1636a778">
   <p data-lake-id="uf5f345db" id="uf5f345db"><span data-lake-id="u9fa1ea49" id="u9fa1ea49">一个类只有一个类对象，但是有很多个实例对象。</span></p>
  </blockquote>
  <p data-lake-id="udd523449" id="udd523449"><span data-lake-id="u3a279394" id="u3a279394">​</span><br></p>
  <h3 data-lake-id="xvjZt" id="xvjZt"><span data-lake-id="uce576068" id="uce576068">同步代码块</span></h3>
  <p data-lake-id="u167cbc97" id="u167cbc97"><br></p>
  <p data-lake-id="u48913829" id="u48913829"><span data-lake-id="ucbf89f41" id="ucbf89f41">除了同步方法以外，synchronized还有同步代码块，同步代码块有两种用法，分别是：</span></p>
  <p data-lake-id="ub788b81d" id="ub788b81d"><span data-lake-id="u9ba01082" id="u9ba01082">​</span><br></p>
  <pre lang="java"><code>
synchronized (this){
}

和

synchronized (ThreadTest.class){
}
</code></pre>
  <p data-lake-id="u55dbe51b" id="u55dbe51b"><br></p>
  <p data-lake-id="uac539ff0" id="uac539ff0"><span data-lake-id="ua2a01da1" id="ua2a01da1">以下是两个示例：</span></p>
  <p data-lake-id="u6100629e" id="u6100629e"><span data-lake-id="u15170c8c" id="u15170c8c">​</span><br></p>
  <pre lang="java"><code>
public class ThreadTest {

    public static void main(String[] args) {

        for (int i = 0; i &lt; 10; i++) {
            MyThread myThread = new MyThread();
            Thread thread = new Thread(myThread);
            thread.start();

        }
    }
}

class MyThread implements Runnable {

    @Override
    public void run() {
        synchronized (this){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " : " + System.currentTimeMillis());
        }
    }
}
</code></pre>
  <p data-lake-id="ud9acd992" id="ud9acd992"><br></p>
  <p data-lake-id="ube3f30a5" id="ube3f30a5"><span data-lake-id="u0e9d2849" id="u0e9d2849">这个代码在执行时，输出结果：</span></p>
  <p data-lake-id="u50aac85b" id="u50aac85b"><span data-lake-id="u6b23dc25" id="u6b23dc25">​</span><br></p>
  <pre lang="java"><code>
Thread-1 : 1692845948150
Thread-2 : 1692845948150
Thread-9 : 1692845948150
Thread-0 : 1692845948150
Thread-3 : 1692845948150
Thread-6 : 1692845948150
Thread-4 : 1692845948150
Thread-5 : 1692845948150
Thread-7 : 1692845948150
Thread-8 : 1692845948150
</code></pre>
  <p data-lake-id="u61bd9976" id="u61bd9976"><br></p>
  <p data-lake-id="u5cda505e" id="u5cda505e"><span data-lake-id="u66ed8b90" id="u66ed8b90">也就是说，多个线程之间是没有互相被锁阻塞而影响的，他们打印出来的时间戳都是一样的。</span></p>
  <p data-lake-id="u7bdc24e6" id="u7bdc24e6"><span data-lake-id="uda95490b" id="uda95490b">​</span><br></p>
  <p data-lake-id="u80f3b2a7" id="u80f3b2a7"><span data-lake-id="ucb60bc87" id="ucb60bc87">而下面这个例子：</span></p>
  <p data-lake-id="u5eef7b16" id="u5eef7b16"><span data-lake-id="ue8f5f144" id="ue8f5f144">​</span><br></p>
  <pre lang="java"><code>
public class ThreadTest {

    public static void main(String[] args) {

        for (int i = 0; i &lt; 10; i++) {
            MyThread myThread = new MyThread();
            Thread thread = new Thread(myThread);
            thread.start();

        }
    }
}

class MyThread implements Runnable {

    @Override
    public void run() {
        synchronized (MyThread.class){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " : " + System.currentTimeMillis());
        }
    }
}
</code></pre>
  <p data-lake-id="u0ddcd6c8" id="u0ddcd6c8"><br></p>
  <p data-lake-id="ueaccdfbb" id="ueaccdfbb"><span data-lake-id="ud4dd4be1" id="ud4dd4be1">输出结果：</span></p>
  <p data-lake-id="u5d55ed93" id="u5d55ed93"><span data-lake-id="ufd9b6b63" id="ufd9b6b63">​</span><br></p>
  <pre lang="java"><code>
Thread-0 : 1692845997813
Thread-9 : 1692845998820
Thread-8 : 1692845999825
Thread-7 : 1692846000831
Thread-6 : 1692846001836
Thread-5 : 1692846002841
Thread-4 : 1692846003848
Thread-3 : 1692846004852
Thread-2 : 1692846005856
Thread-1 : 1692846006859
</code></pre>
  <p data-lake-id="u8612e04a" id="u8612e04a"><br></p>
  <p data-lake-id="ub83dab16" id="ub83dab16"><br></p>
  <p data-lake-id="u6928a108" id="u6928a108"><span data-lake-id="u5f4228bb" id="u5f4228bb">可以看到，每个线程打印出来的时间戳都不一样，是有时间间隔的，这是因为他们互相之间有锁阻塞的影响。</span></p>
  <p data-lake-id="u11d06d4a" id="u11d06d4a"><span data-lake-id="u15fe0732" id="u15fe0732">​</span><br></p>
  <p data-lake-id="ue7a70c3d" id="ue7a70c3d"><span data-lake-id="u50d7781e" id="u50d7781e">​</span><br></p>
  <p data-lake-id="ubc689755" id="ubc689755"><strong><span data-lake-id="u63b546be" id="u63b546be">总结一下，synchronized(this)，其实锁的是this这个实例对象，而synchronized(Xxx.Class)，其实锁的是这个类对象。</span></strong></p>
  <p data-lake-id="u6f627baf" id="u6f627baf"><br></p>
 </body>
</html>